Let's build a tutorial for a flexible `sendMessage` system that lets you specify the **`from`** and **`to`** of each message, like a little internal router 🔀. In other words, this tutorial shows how to build a simple internal message bus between **popup**, **content script**, and **background**, using `chrome.runtime.sendMessage()` and `chrome.runtime.onMessage`. ### ✅ General Goal We’ll build a system where each message includes: ```js { from: "popup", to: "background", type: "some-event", payload: { ... } } ``` The background script acts as a **router**, forwarding messages to the correct target (e.g., from popup → content, from content → popup, etc.). Note this from/to routing is not a native feature of Chrome Extension. We are just leveraging the fact that we can come up with any key value pair in the sendMessage and it's valid to check those properties at onMessage. ### 🧠 Basic Flow Goal We we have a popup that when user clicks Send, it sends message to content.js via background.js "Hi from content". Then content.js sends message back to popup.js via background.js "Hello back from Content" ![[Pasted image 20250407013033.png]] The flow is: ``` Popup → Background → Content Content → Background → Popup ``` --- ### 🧩 File Structure ``` my-extension/ ├── manifest.json ├── background.js ├── popup.html ├── popup.js ├── content.js ``` --- ### 📄 `manifest.json` ```json { "manifest_version": 3, "name": "Directed Messaging Extension", "version": "1.0", "permissions": ["scripting", "tabs"], "host_permissions": [""], "action": { "default_popup": "popup.html" }, "background": { "service_worker": "background.js" }, "content_scripts": [ { "matches": [""], "js": ["content.js"] } ] } ``` --- ### 🧠 `background.js` Acts as a central **message router**. ```js chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { const { from, to, type, payload } = msg; console.log(`[Background] Message from ${from} to ${to}:`, msg); if (to === "content") { // Send to content script chrome.tabs.query({ active: true, currentWindow: true }, tabs => { if (tabs[0]) { chrome.tabs.sendMessage(tabs[0].id, msg); } }); } else if (to === "popup") { // Try sending to popup (must be open) chrome.runtime.sendMessage(msg); } else if (to === "background") { // Handle internally console.log(`[Background] Handling ${type} with payload:`, payload); } sendResponse({ status: "routed" }); return true; // keep message channel open }); ``` --- ### 🌐 `popup.html` ```html Directed Messaging

Send Message to Content

Popup Log



  


```

---

### 📤 `popup.js`

```js
document.getElementById("sendToContent").addEventListener("click", () => {
  chrome.runtime.sendMessage({
    from: "popup",
    to: "content",
    type: "hello",
    payload: { text: "Hi from Popup!" }
  }, response => {
    console.log("Popup got response:", response);
  });
});

chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  if (msg.to === "popup") {
    const log = document.getElementById("log");
    log.textContent += `[Popup Received] ${JSON.stringify(msg)}\n`;
  }
});
```



---

### 📥 `content.js`

```js
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  if (msg.to === "content") {
    console.log("[Content Script] Received:", msg);

    // Reply to popup via background
    chrome.runtime.sendMessage({
      from: "content",
      to: "popup",
      type: "response",
      payload: { text: "Hello back from Content!" }
    });
  }
});
```

---

### ✅ Summary

|Script|Purpose|
|---|---|
|`popup.js`|Sends to `content`, listens for replies|
|`content.js`|Listens for messages from `popup`, replies|
|`background.js`|Routes messages based on `to` field|

---

### 🔧 Bonus: Extend This System

- Add `devtools` or `sidebar` easily
- Add message types like `log`, `status`, `command`
- Use message IDs for response tracking
- Build a lightweight message broker!